package com.rdxer.core.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.rdxer.core.entity.Result;
import com.rdxer.core.entity.StatusCode;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


public class JWTAuthenticationFilter extends BasicAuthenticationFilter {

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager, UserDetailsService userDetailsService, JWTConfiguration jwtConfiguration) {
        super(authenticationManager);
        this.userDetailsService = userDetailsService;

        this.jwtConfiguration = jwtConfiguration;

        result = new Result<>();
        result.setFlag(false);
    }

    Result<String> result;
    private ObjectMapper objectMapper = new ObjectMapper();
    private UserDetailsService userDetailsService;
    private JWTConfiguration jwtConfiguration;


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {

        String header = request.getHeader(jwtConfiguration.header_auth_key);

        if (header == null || !header.startsWith(jwtConfiguration.jwt_prefix)) {
            chain.doFilter(request, response);
            return;
        }
        Authentication authentication = null;
        result.setMessage("NoCatchError:Auth");
        try {
            authentication = getAuthentication(request);
        }
        catch (ExpiredJwtException e){
            e.printStackTrace();
            result.setCode(StatusCode.TokenExpired);
            result.setMessage("token已过有效期");
        }
        catch (JwtException e){
            e.printStackTrace();
            result.setCode(StatusCode.TokenIllegal);
            result.setMessage("token已失效");
        }
        catch (Exception e){
            e.printStackTrace();
            result.setCode(StatusCode.TokenIllegal);
            result.setMessage(e.getMessage());
        }
        if (authentication == null) {
            String json = objectMapper.writeValueAsString(result);
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            response.setCharacterEncoding("UTF-8");
            response.setContentType(MediaType.APPLICATION_JSON_UTF8.toString());
            response.setHeader("ForceLogout","1");
            response.getWriter().write(json);
            return;
        }
        SecurityContextHolder.getContext().setAuthentication(authentication);
        chain.doFilter(request, response);
    }

    /// 获取 -> 登录信息
    private Authentication getAuthentication(HttpServletRequest request) {
        String token = request.getHeader(jwtConfiguration.header_auth_key);

        if (token != null) {
            // parse the token.

            var jwt = Jwts.parser()
                    .setSigningKey(jwtConfiguration.secret)
                    .parseClaimsJws(token.replace(jwtConfiguration.jwt_prefix, "").trim());
            Claims body = jwt.getBody();
            String username =  body.getSubject();
            String audience = body.getAudience();
            // 如果是签发给客户端用的不能作为身份验证使用，只能访问对应的接口
            if (audience != null && audience.startsWith("client:")) {
                return null;
            }
            if (username != null) {

                UserDetails userDetails =  userDetailsService.loadUserByUsername(username);

                return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            }
            return null;
        }
        return null;
    }

    @Override
    protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException {
        super.onUnsuccessfulAuthentication(request, response, failed);
    }
}
